using System;
using System.Text;
using System.Collections.Generic;
using System.ComponentModel;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Reflection;

using Open.Data.Persistence;

using Open.Data.Persistence.Hashes;

namespace Open.Data.Persistence.Testing
{
    //Tests the abbreviations engine
    [TestClass]
    public class HashTests
    {
        [TestMethod]
        public void HashParameterTests()
        {
            StringParameter parm1 = new StringParameter("");
            StringParameter parm2 = new StringParameter("");
            
            ParameterTest<string>(parm1, parm2, "test", "test2");

            IntegerParameter parm3 = new IntegerParameter(0);
            IntegerParameter parm4 = new IntegerParameter(0);

            ParameterTest<int>(parm3, parm4, 1, 2);
        }

        [TestMethod]
        public void HashLiteralTests()
        {
            StringLiteral parm1 = new StringLiteral("");
            StringLiteral parm2 = new StringLiteral("");
            LiteralTest<string>(parm1, parm2, "test", "test2");

            IntegerLiteral parm3 = new IntegerLiteral(0);
            IntegerLiteral parm4 = new IntegerLiteral(0);
            LiteralTest<int>(parm3, parm4, 1, 2);

            Assert.AreNotEqual(new StringParameter("Test"), new StringParameter("Test"), "Literal and parameter with same value return same hash.");
        }

        [TestMethod]
        public void HashColumnTests()
        {
            Column column1 = new Column(typeof(Person), "FirstNames");
            Column column2 = new Column(typeof(Person), "FirstNames");
            Assert.AreEqual(GetHash(column1), GetHash(column2), "Matching columns do not return same hash.");

            Column column3 = new Column(typeof(Person), "LastNames");
            Assert.AreNotEqual(GetHash(column1), GetHash(column3), "Different columns return same hash.");
        }

        [TestMethod]
        public void HashComparisonTests()
        {
            Column column1 = new Column(typeof(Person), "FirstNames");
            Column column2 = new Column(typeof(Person), "FirstNames");
            StringParameter parm1 = new StringParameter("Bob");
            StringParameter parm2 = new StringParameter("Bob");

            Comparison comparison1 = new Comparison(column1, parm1);
            Comparison comparison2 = new Comparison(column2, parm2);
            Assert.AreEqual(GetHash(comparison1), GetHash(comparison2), "Matching comparisons have different hash values.");

            Column column3 = new Column(typeof(Person), "LastNames");
            StringParameter parm3 = new StringParameter("Bob");
            Comparison comparison3 = new Comparison(column3, parm3);
            Assert.AreNotEqual(GetHash(comparison1), GetHash(comparison3), "Different comparisons have same hash values.");

            NullComparison nullComparison1 = new NullComparison(column1);
            NullComparison nullComparison2 = new NullComparison(column2);
            Assert.AreEqual(GetHash(nullComparison1), GetHash(nullComparison2), "Matching null comparisons have different hash values.");

            NullComparison nullComparison3 = new NullComparison(column3);
            Assert.AreNotEqual(GetHash(nullComparison1), GetHash(nullComparison3), "Different null comparisons have same hash values.");

            NotNullComparison notnullComparison1 = new NotNullComparison(column1);
            NotNullComparison notnullComparison2 = new NotNullComparison(column2);
            Assert.AreEqual(GetHash(notnullComparison1), GetHash(notnullComparison2), "Matching not null comparisons have different hash values.");

            NotNullComparison notnullComparison3 = new NotNullComparison(column3);
            Assert.AreNotEqual(GetHash(nullComparison1), GetHash(nullComparison3), "Different not null comparisons have same hash values.");

            Assert.AreNotEqual(GetHash(nullComparison1), GetHash(notnullComparison1), "Null and not null comparisons have same hash values.");
        }

        [TestMethod]
        public void HashConditionsTest()
        {
            Conditions conditions1 = new Conditions();
            Conditions conditions2 = new Conditions();

            Column column1 = new Column(typeof(Person), "FirstNames");
            Column column2 = new Column(typeof(Person), "FirstNames");
            StringParameter parm1 = new StringParameter("Bob");
            StringParameter parm2 = new StringParameter("Bob");

            Comparison comparison1 = new Comparison(column1, parm1);
            Comparison comparison2 = new Comparison(column2, parm2);

            conditions1.Add(comparison1);
            conditions2.Add(comparison2);

            Assert.AreEqual(GetHash(comparison1), GetHash(comparison2), "Matching conditions have different hash values.");
        }

        [TestMethod]
        public void HashQueryTests()
        {
            Query<Person> query1 = new Query<Person>();
            Query<Person> query2 = new Query<Person>();

            Assert.AreEqual(query1.GetHashCode(), query2.GetHashCode(), "Matching queries have different hashes.");

            Column column1 = new Column(typeof(Person), "FirstNames");
            Column column2 = new Column(typeof(Person), "FirstNames");

            query1.Where.AddComparison(column1, "Bob");
            query2.Where.AddComparison(column2, "Bob");

            Assert.AreEqual(query1.GetHashCode(), query2.GetHashCode(), "Matching queries have different hashes..");
        }

        private void ParameterTest<T>(Parameter<T> parm1, Parameter<T> parm2, T value, T value2)
        {
            parm1.Value = value;
            parm2.Value = value;
            Assert.AreEqual(GetHash(parm1), GetHash(parm2), string.Format("Matching parameter hashes for type {0} do not match", typeof(T).Name));

            parm2.Value = value2;
            Assert.AreEqual(GetHash(parm1), GetHash(parm2), string.Format("Matching parameter hashes for type {0} do not match with different values", typeof(T).Name));

            parm1.Name = "myname";
            parm1.Value = value;
            parm2.Name = "myname";
            parm2.Value = value;
            Assert.AreEqual(GetHash(parm1), GetHash(parm2), string.Format("Matching parameter hashes for type {0} with matching names do not match.", typeof(T).Name));

            parm2.Value = value2;
            Assert.AreEqual(GetHash(parm1), GetHash(parm2), string.Format("Matching parameter hashes for type {0} with matching names and different values do not match.", typeof(T).Name));

            parm2.Value = value;
            parm2.Name = "anothername";

            Assert.AreNotEqual(GetHash(parm1), GetHash(parm2), string.Format("Matching parameter hashes for type {0} with different names and matching values still match.", typeof(T).Name));

            parm2.Value = value2;
            Assert.AreNotEqual(GetHash(parm1), GetHash(parm2), string.Format("Matching parameter hashes for type {0} with different names and different values still match.", typeof(T).Name));
        }

        private void LiteralTest<T>(Literal<T> literal1, Literal<T> literal2, T value, T value2)
        {
            literal1.Value = value;
            literal2.Value = value;
            Assert.AreEqual(GetHash(literal1), GetHash(literal2), string.Format("Matching literal hashes for type {0} do not match", typeof(T).Name));

            literal2.Value = value2;
            Assert.AreNotEqual(GetHash(literal1), GetHash(literal2), string.Format("Different literal hashes for type {0} match", typeof(T).Name));
        }

        private int GetHash<T>(Parameter<T> hashable)
        {
            StringBuilder builder = new StringBuilder();
            hashable.AppendHashCode(builder);

            return builder.ToString().GetHashCode();
        }

        private int GetHash<T>(Literal<T> hashable)
        {
            StringBuilder builder = new StringBuilder();
            hashable.AppendHashCode(builder);

            return builder.ToString().GetHashCode();
        }

        private int GetHash(Column hashable)
        {
            StringBuilder builder = new StringBuilder();
            hashable.AppendHashCode(builder);

            return builder.ToString().GetHashCode();
        }

        private int GetHash(Comparison hashable)
        {
            StringBuilder builder = new StringBuilder();
            hashable.AppendHashCode(builder);

            return builder.ToString().GetHashCode();
        }

        private int GetHash(NullComparison hashable)
        {
            StringBuilder builder = new StringBuilder();
            hashable.AppendHashCode(builder);

            return builder.ToString().GetHashCode();
        }
    }
}
